#include "VTTAtlas.h"
#include "VTTCommon.h"
#include "VTTErrorManager.h"
#include "VTTImage.h"
#include "VTTInfoFile.h"
#include "VTTMemoryManager.h"
#include "VTTPackNode.h"
#include "VTTTimer.h"

#include <iostream>
#include <fstream>
#include <math.h>
#include <set>

using std::cout;
using std::endl;
using std::map;
using std::max;
using std::min;
using std::ofstream;
using std::pair;
using std::set;
using std::string;
using std::vector;

/**
  *	Struct that that stores the width, the height and a pointer to a particular image object
  */
struct VTTImageArea
{
	/** Width of the image in pixels */
	UINT32 width;
	/** Height of the image in pixels */
	UINT32 height;
	/** Pointer to a particular image object */
	VTTImage *image;

	/**
	  *	Constructs a new VTTImageArea object with the provided parameters
	  *
	  * @param _width width of the image in pixels
	  * @param _height height of the image in pixels
	  * @param _image pointer to a particular image object
	  */
	VTTImageArea( UINT32 _width, UINT32 _height, VTTImage *_image ) : width(_width), height(_height), image(_image) {};
};

/**
  *	Instances of VTTImageAreaHeightCompare are needed as a compare-object
  * in stl containers. The objects are first sorted from the biggest to the
  * smallest height, then from the biggest to the smallest width and then
  * (when both image pointers are not Null) from the "smallest" to the "biggest"
  * image name. See the definition of the <operator of std::string for details.
  *
  * @param area1 first image area in the comparison
  * @param area2 second image area in the comparison
  * @return boolean value that behaves in the way, so that the objects are sorted as defined above
  */
class VTTImageAreaHeightCompare
{
public:
	bool operator()(const VTTImageArea &area1, const VTTImageArea &area2)
	{
		bool result = false;

		if (area1.height < area2.height)
		{
			result = true;
		}
		else if (area1.height > area2.height)
		{
			result = false;
		}
		else
		{
			if (area1.width < area2.width)
			{
				result = true;
			}
			else if (area1.width > area2.width)
			{
				result = false;
			}
			else
			{
				if (area1.image == 0 || area2.image == 0)
				{
					if (area1.image < area2.image)
					{
						result = true;
					}
					else
					{
						result = false;
					}
				}
				else
				{
					if (area1.image->GetImagePath() < area2.image->GetImagePath())
					{
						result = false;
					}
					else
					{
						result = true;
					}
				}
			}
		}

		return !result;
	}
};

bool rectIntersectsRect( const UINT32 *r1, const UINT32 *r2 );

/**
  *	Constructs a new Atlas object with the configuration provided.
  *
  * @param vttConfig Configuration that is used to generate the Atlas
  */
VTTAtlas::VTTAtlas( const VTTConfiguration &vttConfig )
{
	mEmptyAreaGreyValue = 255;
	mNumComponents = 3;
	mRootPackNode = 0;

	Init( vttConfig );
}

/**
  *	Copy constructor. Everything is copied, also the image data when there is any.
  *
  * @param atlas Atlas object that is copied
  */
VTTAtlas::VTTAtlas( const VTTAtlas &atlas )
{
	mEmptyAreaGreyValue = 255;
	mNumComponents = 3;

	mConfiguration = atlas.mConfiguration;
	
	mRootPackNode = new VTTPackNode( *atlas.mRootPackNode );

	mImageNames = atlas.mImageNames;

	for (vector<VTTImage*>::const_iterator it = atlas.mImages.begin();
		it != atlas.mImages.end(); it++)
	{
		mImages.push_back( new VTTImage( *(*it) ) );
	}

	mNamesToImagesMap = atlas.mNamesToImagesMap;

	mIsNull = atlas.mIsNull;

	mPartsPerSide = atlas.mPartsPerSide;
	mPartSideLength = atlas.mPartSideLength;
	mSideLength = atlas.mSideLength;

	mFullPath = atlas.mFullPath;

	mIsWriting = false;
	mWriteResult = true;
}

/**
  *	Destroys the Atlas object.
  */
VTTAtlas::~VTTAtlas( void )
{
	if (mRootPackNode) delete mRootPackNode;

	for (vector<VTTImage*>::iterator it = mImages.begin(); it != mImages.end(); ++it)
		delete *it;
}

/**
  *	Returns a const reference to the configuration of the Atlas.
  *
  * @return const reference to the configuration of the Atlas
  */
const VTTConfiguration &VTTAtlas::GetConfiguration( void ) const
{
	return mConfiguration;
}

/**
  *	Returns a const reference to the map, that maps names (not paths!) of images to the corresponding image objects.
  *
  * @return const reference to the map, that maps names of images to the corresponding image objects
  */
const map<string, VTTImage*> &VTTAtlas::GetNamesToImagesMap( void ) const
{
	return mNamesToImagesMap;
}

/**
  *	Returns the full directory path of the Atlas.
  *
  * @return full directory path of the Atlas
  */
const string &VTTAtlas::GetFullPath( void ) const
{
	return mFullPath;
}

/**
  *	Returns the number of Atlas parts per side, always a number that is power of two: 2, 4, 8 etc.
  *
  * @return number of Atlas parts per side, always a number that is power of two: 2, 4, 8 etc.
  */
UINT32 VTTAtlas::GetPartsPerSide( void ) const
{
	return mPartsPerSide;
}

/**
  *	Returns the side length of a single Atlas part in pixels.
  *
  * @return side length of a single Atlas part in pixels
  */
UINT32 VTTAtlas::GetPartSideLength( void ) const
{
	return mPartSideLength;
}

/**
  *	Returns the side length of the whole Atlas in pixels.
  *
  * @return side length of the whole Atlas in pixels
  */
UINT32 VTTAtlas::GetSideLength( void ) const
{
	return mSideLength;
}

/**
  *	Returns the result of the last Atlas generation process. Boolean value that is true when the generation was successful, otherwise false.
  *
  * @return result of the last Atlas generation process
  */
bool VTTAtlas::GetWriteResult( void ) const
{
	return mWriteResult;
}

/**
  *	Initializes the Atlas object. It evaluates all the images in the directory,
  * finds a side length that suits the requirements, packs all images into the Atlas etc.
  * After that the Atlas can be written to disc.
  *
  * @param vttConfig configuration object
  * @return boolean value indicating whether the initialization was successful or not
  */
bool VTTAtlas::Init( const VTTConfiguration &vttConfig )
{
	if (vttConfig.IsNull())
	{
		VTTErrorManager::Get()->pushErrorMessage( "VTTAtlas::Init(const VTTConfiguration&): VTTAtlas can not be initialized with Null-Configuration" );
		Reset();
		return false;
	}

	if ((mImageNames = GetAllImageNamesInsideDir( vttConfig.GetBasePath() )).size() == 0)
	{
		VTTErrorManager::Get()->pushErrorMessage( "VTTAtlas::Init(const VTTConfiguration&): VTTAtlas can not be initialized with an empty input images directory" );
		Reset();
		return false;
	}

	mConfiguration = vttConfig;
	mFullPath = mConfiguration.GetBasePath() + VTT_FILE_SEPARATOR +
		mConfiguration.GetAtlasName();

	const UINT32 MAX_ATLAS_SIDE_LENGTH = 128 * 1024;

	UINT32 num_pixels = 0U;
	UINT64 sum_pixels = 0U;

	UINT32 image_height = 0U;
	UINT32 image_width = 0U;

	UINT32 max_image_height = 0U;
	UINT32 max_image_width = 0U;
	
	string image_path;

	bool all_ok = true;

	VTTImage *current_image = 0;

	const vector<string> &imageblock_imagenames = mConfiguration.GetImageBlockImageNames();
	const UINT32 imageblock_height = mConfiguration.GetImageBlockHeight();
	const UINT32 imageblock_width = mConfiguration.GetImageBlockWidth();
	map<string, UINT32> imageblock_name_to_count_map;

	UINT32 agg_height = 0U;
	UINT32 max_row_width = 0U;

	set<VTTImageArea, VTTImageAreaHeightCompare> image_area_set;

	if (IsDir( mConfiguration.GetBasePath() ) && mImageNames.size() > 0)
	{
		for (vector<string>::const_iterator it = mImageNames.begin(); it != mImageNames.end(); ++it)
		{
			image_path = mConfiguration.GetBasePath() + VTT_FILE_SEPARATOR + *it;
			
			if (IsValidImageName( image_path ) &&
				IsFile( image_path ))
			{
				current_image = new VTTImage( image_path );

				if (!current_image->IsNull())
				{
					image_width = current_image->GetWidth();
					image_height = current_image->GetHeight();

					sum_pixels += image_width * image_height;

					if (max_image_width < image_width)
						max_image_width = image_width;

					if (max_image_height < image_height)
						max_image_height = image_height;

					mImages.push_back( current_image );
					mNamesToImagesMap.insert( pair<string, VTTImage*>( *it, current_image ) );
				} else
				{
					all_ok = false;
					break;
				}
			} else
			{
				all_ok = false;
				break;
			}
		}

		if (all_ok && imageblock_imagenames.size() > 0)
		{
			// imageBlock option is used

			UINT32 current_row_height = 0U;
			UINT32 row_width = 0U;

			map<string, VTTImage*>::iterator names_to_images_map_it;

			for (UINT32 y = 0; y < imageblock_height; y++)
			{
				if (!all_ok)
				{
					break;
				}

				for (UINT32 x = 0; x < imageblock_width; x++)
				{
					names_to_images_map_it = mNamesToImagesMap.find( imageblock_imagenames[y * imageblock_width + x] );

					imageblock_name_to_count_map.insert( make_pair( imageblock_imagenames[y * imageblock_width + x], 0U ) ).first->second++;

					if (names_to_images_map_it == mNamesToImagesMap.end())
					{
						// Image used in image block is not in the directory -> Error!
						VTTErrorManager::Get()->pushErrorMessage( "VTTAtlas::Init(const VTTConfiguration&): Image " + imageblock_imagenames[y * imageblock_width] + " not found!" );

						all_ok = false;
						break;
					}

					if (x == 0)
					{
						current_row_height = names_to_images_map_it->second->GetHeight();
						agg_height += current_row_height;
						row_width = 0U;
					}
					else if (names_to_images_map_it->second->GetHeight() != current_row_height)
					{
						VTTErrorManager::Get()->pushErrorMessage( "VTTAtlas::Init(const VTTConfiguration&): All images in a row must have the same height for the imageBlock option" );
						all_ok = false;
						break;
					}

					row_width += names_to_images_map_it->second->GetWidth();
				}

				if (row_width > max_row_width) max_row_width = row_width;
			}

			image_area_set.insert( VTTImageArea( max_row_width, agg_height, 0 ) );
		}

		if (all_ok)
		{
			// Add pixelnumbers of all images that occur more than once
			map<string, VTTImage*>::iterator names_to_images_map_it;
			for (map<string, UINT32>::const_iterator it = imageblock_name_to_count_map.begin();
				it != imageblock_name_to_count_map.end(); it++)
			{
				if (it->second > 1U)
				{
					names_to_images_map_it = mNamesToImagesMap.find( it->first );
					sum_pixels += (it->second - 1U) * names_to_images_map_it->second->GetWidth() * names_to_images_map_it->second->GetHeight();
				}
			}

			// Sort the image areas by adding it to the set
			for (names_to_images_map_it = mNamesToImagesMap.begin();
				names_to_images_map_it != mNamesToImagesMap.end(); names_to_images_map_it++)
			{
				if (imageblock_name_to_count_map.find( names_to_images_map_it->first ) == imageblock_name_to_count_map.end())
				{
					// Image is not used inside the image block, add it as a seperate image area
					image_area_set.insert( VTTImageArea( names_to_images_map_it->second->GetWidth(),
						names_to_images_map_it->second->GetHeight(), names_to_images_map_it->second ) );
				}
			}

			// Calculate final Atlas size
			UINT32 theor_atlas_side_length = static_cast<UINT32>( ceil( sqrt( static_cast<long double>( sum_pixels ) ) ) );
			UINT32 max_sidelength = max( max( max( max( max_image_width, max_image_height ), theor_atlas_side_length ), agg_height), max_row_width );
			UINT32 final_atlas_side_length = GetNextPowerOfTwo( max_sidelength );
			UINT32 final_atlas_part_side_length_without_border = final_atlas_side_length;
			UINT32 final_atlas_part_side_length = 0;

			while (final_atlas_side_length -
				(2 * mConfiguration.GetBorder() * (final_atlas_side_length / mConfiguration.GetTileSize())) < max_sidelength )
			{
				final_atlas_side_length = GetNextPowerOfTwo( final_atlas_side_length );
			}

			final_atlas_part_side_length_without_border = final_atlas_side_length;

			if (final_atlas_side_length > MAX_ATLAS_SIDE_LENGTH)
			{
				//ERROR: Atlas is too big
				VTTErrorManager::Get()->pushErrorMessage( "VTTAtlas::Init(const VTTConfiguration&): Could not initialize Atlas, necessary side length is bigger than 128k" );

				mConfiguration = VTTConfiguration();
				mImageNames.clear();
				for (vector<VTTImage*>::iterator it = mImages.begin(); it != mImages.end(); ++it)
					delete *it;
				mImages.clear();
				mIsNull = true;
				mIsWriting = false;
				mWriteResult = true;
				mSideLength = 0;
				mPartsPerSide = 0;
				mPartSideLength = 0;

				return false;
			}

			if (mConfiguration.GetBorder() > 0)
				final_atlas_side_length -= 2 * mConfiguration.GetBorder() * (final_atlas_side_length / mConfiguration.GetTileSize());

			final_atlas_part_side_length = final_atlas_side_length;
				
			while (final_atlas_part_side_length > mConfiguration.GetMaxAtlasPartSize())
			{
				final_atlas_part_side_length /= 2;
			}

			VTTPackNode *image_block_node = 0;
			const VTTPackNode *current_pack_node = 0;

			bool valid_atlassize_found = false;
			bool first_part_successful = false;

			while (!valid_atlassize_found)
			{
				if(mRootPackNode)
				{
					delete mRootPackNode;
					mRootPackNode = 0;
				}

				mRootPackNode = new VTTPackNode( final_atlas_side_length, final_atlas_side_length );
				image_block_node = 0;
				first_part_successful = true;

				// Pack the images outside the image block
				for (set<VTTImageArea, VTTImageAreaHeightCompare>::iterator area_it = image_area_set.begin();
					area_it != image_area_set.end(); area_it++)
				{
					current_pack_node = mRootPackNode->Insert( area_it->width, area_it->height );

					if (area_it->image != 0)
					{
						if (current_pack_node)
						{
							area_it->image->AddPackNode( current_pack_node );
						}
						else
						{
							for (set<VTTImageArea, VTTImageAreaHeightCompare>::iterator clear_area_it = image_area_set.begin();
								clear_area_it != image_area_set.end(); clear_area_it++)
							{
								if (clear_area_it->image != 0) clear_area_it->image->ClearPackNodes();
							}

							first_part_successful = false;
							break;
						}
					}
					else
					{
						if (current_pack_node)
						{
							image_block_node = new VTTPackNode( *current_pack_node );
						}
						else
						{
							for (set<VTTImageArea, VTTImageAreaHeightCompare>::iterator clear_area_it = image_area_set.begin();
								clear_area_it != image_area_set.end(); clear_area_it++)
							{
								if (clear_area_it->image != 0) clear_area_it->image->ClearPackNodes();
							}
							first_part_successful = false;
							break;
						}
					}
				}

				if (!first_part_successful)
				{
					if (image_block_node)
					{
						delete image_block_node;
						image_block_node = 0;
					}

					final_atlas_part_side_length_without_border *= 2;

					if (mConfiguration.GetBorder() > 0)
						final_atlas_side_length -= 2 * mConfiguration.GetBorder() * (final_atlas_part_side_length_without_border / mConfiguration.GetTileSize());
					else
						final_atlas_side_length = final_atlas_part_side_length_without_border;

					final_atlas_part_side_length = final_atlas_side_length;

					while (final_atlas_part_side_length > mConfiguration.GetMaxAtlasPartSize())
					{
						final_atlas_part_side_length /= 2;
					}

					if (final_atlas_side_length > MAX_ATLAS_SIDE_LENGTH)
					{
						// ERROR, no Atlas size suits
						VTTErrorManager::Get()->pushErrorMessage( "VTTAtlas::Init(const VTTConfiguration&): Could not initialize Atlas, necessary side length is bigger than 128k" );

						mConfiguration = VTTConfiguration();
						mImageNames.clear();
						for (vector<VTTImage*>::iterator it = mImages.begin(); it != mImages.end(); ++it)
							delete *it;
						mImages.clear();
						mIsNull = true;
						mIsWriting = false;
						mWriteResult = true;
						mSideLength = 0;
						mPartsPerSide = 0;
						mPartSideLength = 0;

						return false;
					}
					else
					{
						continue;
					}
				}

				valid_atlassize_found = true;

				// Pack the images inside the image block
				UINT32 current_height = 0U;
				UINT32 current_width = 0U;

				UINT32 tmp[2];

				for (UINT32 y = 0; y < imageblock_height; y++)
				{
					current_width = 0;

					for (UINT32 x = 0; x < imageblock_width; x++)
					{
						current_image = mNamesToImagesMap[imageblock_imagenames[y * imageblock_width + x]];

						tmp[0] = image_block_node->GetArea()[0] + current_width;
						tmp[1] = image_block_node->GetArea()[1] + current_height;

						// Add packnode
						current_image->AddPackNode( &VTTPackNode( tmp[0], tmp[1], tmp[0] + current_image->GetWidth(), tmp[1] + current_image->GetHeight() ) );

						current_width += current_image->GetWidth();
					}
					current_height += current_image->GetHeight();
				}

				if (image_block_node) delete image_block_node;
			}

			mSideLength = final_atlas_side_length;
			mPartSideLength = final_atlas_part_side_length;
			mPartsPerSide = final_atlas_side_length / final_atlas_part_side_length;
			mIsNull = false;
			mIsWriting = false;
			mWriteResult = true;

			return true;
		}
	}

	//cout << "ERROR: Could not initialize Atlas" << endl;

	VTTErrorManager::Get()->pushErrorMessage( "VTTAtlas::Init(const VTTConfiguration&): Could not initialize Atlas, check Configuration parameters" );

	mConfiguration = VTTConfiguration();
	mImageNames.clear();
	for (vector<VTTImage*>::iterator it = mImages.begin(); it != mImages.end(); ++it)
		delete *it;
	mImages.clear();
	mIsNull = true;
	mIsWriting = false;
	mWriteResult = true;
	mSideLength = 0;
	mPartsPerSide = 0;
	mPartSideLength = 0;

	return false;
}

/**
  *	Returns a boolean value indicating whether the Atlas is currently generated or not
  *
  * @return boolean value indicating whether the Atlas is currently generated or not
  */
bool VTTAtlas::IsWriting( void ) const
{
	return mIsWriting;
}

/**
  *	Reloads a particular image. This can be necessary when the image was changed.
  *
  * @param imageName name (not the path!) of the image that has to be reloaded
  * @return boolean value indicating whether the reload was successful or not
  */
bool VTTAtlas::ReloadImage( const string &imageName )
{
	map<string, VTTImage*>::iterator it = mNamesToImagesMap.find( imageName );

	if (mNamesToImagesMap.end() == it)
		return false;

	return it->second->Load();
}

/**
  *	Resets the Atlas. Therefore all data (image data, pack nodes) is deleted.
  * The Atlas is a Null-object after that.
  */
void VTTAtlas::Reset( void )
{
	for (vector<VTTImage*>::iterator it = mImages.begin(); it != mImages.end(); ++it)
		delete *it;

	mImages.clear();

	mIsNull = true;
	mIsWriting = false;
	mWriteResult = true;
	mSideLength = 0;
	mPartsPerSide = 0;
	mPartSideLength = 0;

	mConfiguration = VTTConfiguration();
	
	if (mRootPackNode)
	{
		delete mRootPackNode;
		mRootPackNode = 0;
	}
	
	mEmptyAreaGreyValue = 255;

	mNamesToImagesMap.clear();
	mImageNames.clear();
	mNumComponents = 3;

	mFullPath = "";
}

/**
  *	Starts the Atlas generation in a separate thread, therefore the method returns
  * immediately. A callback function can be provided that is called when the Atlas
  * generation is ready or when it fails.
  *
  * @param overwrite boolean value indicating whether files, that already exist should be overwritten or not
  * @param callbackFunction pointer to a callback function that is called when the Atlas generation is ready (true) or when it fails (false)
  */
void VTTAtlas::WriteInSeparateThread( bool overwrite, void (*callbackFunction)(bool) )
{
	if (mIsWriting || mWriteThread.joinable())
	{
		VTTErrorManager::Get()->pushErrorMessage("ERROR: VTTAtlas::WriteInSeparateThread(bool, void (*)(bool)): Already writing Atlas");
		if (callbackFunction != 0)
			callbackFunction( false );
	}
	else
	{
		mWriteThread = boost::thread( &VTTAtlas::WriteThread, this, overwrite, callbackFunction );
	}
}

/**
  *	The thread method that is used for the Atlas generation when WriteInSeparateThread() is called
  *
  * @param atlas Pointer to the Atlas object
  * @param overwrite boolean value indicating whether files, that already exist should be overwritten or not
  * @param callbackFunction pointer to a callback function that is called when the Atlas generation is ready (true) or when it fails (false)
  */
void VTTAtlas::WriteThread( VTTAtlas *atlas, bool overwrite, void (*callbackFunction)(bool) )
{
	atlas->Write( overwrite, callbackFunction );
}

/**
  *	Generates the Virtual Texture Atlas and writes it to disc. The directory
  * that is provided in the configuration is used as destination.
  *
  * @param overwrite boolean value indicating whether files, that already exist should be overwritten or not
  * @param callbackFunction pointer to a callback function that is called when the Atlas generation is ready (true) or when it fails (false)
  */
void VTTAtlas::Write( bool overwrite, void (*callbackFunction)(bool) )
{
	mIsWriting = true;

	if (IsNull() || mImages.size() == 0)
	{
		VTTErrorManager::Get()->pushErrorMessage( "VTTAtlas::Write(bool): Can not write Null Atlas" );
		mIsWriting = false;
		mWriteResult = false;
		if (callbackFunction != 0)
			callbackFunction( false );
		return;
	}

	string image_name;

	if (!IsDir( mFullPath ) && !MakeDir( mFullPath ))
	{
		//cout << "ERROR: Could not create dir " << mFullPath << endl;
		VTTErrorManager::Get()->pushErrorMessage( "VTTAtlas::Write(bool): Could not create dir " + mFullPath );
		mIsWriting = false;
		mWriteResult = false;
		if (callbackFunction != 0)
			callbackFunction( false );
		return;
	}

	UINT32 num_elements_in_atlas_part = mPartSideLength * mPartSideLength * mNumComponents;
	unsigned char *atlas_part =
		static_cast<unsigned char*>( malloc( num_elements_in_atlas_part ) );

	if (0 == atlas_part)
	{
		//cout << "ERROR: Not enough memory for Atlas generation" << endl;
		VTTErrorManager::Get()->pushErrorMessage( "VTTAtlas::Write(bool): Not enough memory for Atlas generation" );
		mIsWriting = false;
		mWriteResult = false;
		if (callbackFunction != 0)
			callbackFunction( false );
		return;
	}

	VTTTimer timer;
	double time = 0.0;

	vector<VTTImage*>::const_iterator im_it;

	//VTTImage *current_sub;
	const unsigned char *current_sub_data;

	UINT32 out_rect[4];

	// Size of the Sub-Image
	UINT32 sub_image_width;
	UINT32 sub_image_height;

	// Loop indices for filling the atlas_part
	UINT32 sub_x_start;
	UINT32 sub_x_end;

	UINT32 sub_y_start;
	UINT32 sub_y_end;

	UINT32 atlas_part_index;
	UINT32 atlas_part_line_inc;

	const UINT32 *image_area;

	INT32 x_coord_in_part;
	INT32 y_coord_in_part;

	UINT32 count = 0;
	UINT32 num_atlas_files = mPartsPerSide * mPartsPerSide;

	UINT32 sub_data_index;
	UINT32 sub_data_inc;

	UINT32 line_length;

	string file_format =
		GetFileFormatEnding( mConfiguration.GetAtlasFormat() );

	// Write Info-File
	if (overwrite || !IsFile( mFullPath + VTT_FILE_SEPARATOR + "textureAtlasInfo.txt" ))
	{
		//Update the last changing dates of the images
		for (map<string, VTTImage*>::const_iterator it = mNamesToImagesMap.begin();
			it != mNamesToImagesMap.end(); it++)
		{
			it->second->UpdateLastChangingDate();
		}

		VTTInfoFile::WriteInitialFile( mFullPath, &mNamesToImagesMap );
	}

	cout << "INFO: Generating Virtual Texture Atlas..." << endl << endl;

	timer.Toc();

	for (UINT32 y = 0; y < mPartsPerSide; y++)
	{
		for (UINT32 x = 0; x < mPartsPerSide; x++)
		{
			count++;
			
			time += timer.Toc();

			if (time >= 2.0 || (y == mPartsPerSide - 1 && x == mPartsPerSide - 1) )
			{
				cout << "\r-\tAtlas Part " + UINT32ToString( count ) + "/" +
					UINT32ToString( num_atlas_files );
				flush( cout );

				time -= 2.0;
			}

			image_name = "_texture_atlas_" + UINT32ToString( x ) + "_" +
				UINT32ToString( y ) + file_format;

			if (!overwrite && IsFile( mFullPath + VTT_FILE_SEPARATOR + image_name ))
				continue;

			out_rect[0] = x * mPartSideLength;
			out_rect[1] = y * mPartSideLength; 
			out_rect[2] = (x + 1) * mPartSideLength;
			out_rect[3] = (y + 1) * mPartSideLength;

			// Reset atlas_part
			memset( atlas_part, mEmptyAreaGreyValue, num_elements_in_atlas_part );

			for (im_it = mImages.begin(); im_it != mImages.end(); im_it++)
			{
				for (vector<VTTPackNode*>::const_iterator packnode_it = (*im_it)->GetPackNodeVector().begin();
					packnode_it != (*im_it)->GetPackNodeVector().end(); packnode_it++)
				{
					image_area = (*packnode_it)->GetArea();
				
					if (rectIntersectsRect( image_area, out_rect ))
					{
						x_coord_in_part = image_area[0] - out_rect[0];
						y_coord_in_part = image_area[1] - out_rect[1];

						sub_image_width = min( image_area[2], out_rect[2] ) - max( image_area[0], out_rect[0] );
						sub_image_height = min( image_area[3], out_rect[3] ) - max( image_area[1], out_rect[1] );

						// Load image if it has not been done
						if (!(*im_it)->HasInternalData())
						{
							if (!(*im_it)->Load())
							{
								//cout << "ERROR: Could not open Image file, stopping Atlas Generation..." << endl;
								VTTErrorManager::Get()->pushErrorMessage( "VTTAtlas::Write(bool): Could not open input image file " + (*im_it)->GetImagePath() );
								free( atlas_part );
								VTTMemoryManager::Get()->FreeAllMemory();

								mIsWriting = false;
								mWriteResult = false;
								if (callbackFunction != 0)
									callbackFunction( false );
								return;
							}
						}

						//current_sub_data = current_sub->GetDataPointer();
						current_sub_data = (*im_it)->GetDataPointer();

						sub_y_start = y_coord_in_part < 0 ? 0 : y_coord_in_part;
						sub_y_end = sub_y_start + sub_image_height;

						sub_x_start = x_coord_in_part < 0 ? 0 : x_coord_in_part;
						sub_x_end = sub_x_start + sub_image_width;

						atlas_part_index = (sub_y_start * mPartSideLength + sub_x_start) * mNumComponents;
						atlas_part_line_inc = mPartSideLength * mNumComponents;

						sub_data_inc = (*im_it)->GetWidth() * mNumComponents;
						sub_data_index = sub_data_inc * (y_coord_in_part < 0 ? abs( y_coord_in_part ) : 0) +
							(x_coord_in_part < 0 ? abs( x_coord_in_part ) : 0) * mNumComponents;
					
						line_length = sub_image_width * mNumComponents;
						// Fill the atlas_part with the (Sub-)Image
						for (UINT32 y_in_sub = 0; y_in_sub < sub_image_height; y_in_sub++)
						{
							memcpy( &atlas_part[atlas_part_index], &current_sub_data[sub_data_index], line_length );

							atlas_part_index += atlas_part_line_inc;
							sub_data_index += sub_data_inc;
						}

						//delete current_sub;
					}
				}
			}

			//string imageName = "_texture_atlas_" + IntToString( x ) + "_" + IntToString( y ) + ".jpg";

			if (!VTTImage::WriteImageFile( mFullPath + VTT_FILE_SEPARATOR + image_name,
				atlas_part, mPartSideLength, mPartSideLength, mNumComponents,
				mConfiguration.GetPictureQuality() ))
			{
				VTTErrorManager::Get()->pushErrorMessage( "VTTAtlas::Write(bool): Could not write Atlas part " + image_name );

				free( atlas_part );
				VTTMemoryManager::Get()->FreeAllMemory();

				mIsWriting = false;
				mWriteResult = false;
				if (callbackFunction != 0)
					callbackFunction( false );
				return;
			}
		}
	}
	
	cout << endl << endl;

	cout << "INFO: Virtual Texture Atlas generated" << endl;

	free( atlas_part );

	VTTMemoryManager::Get()->FreeAllMemory();

	mIsWriting = false;
	mWriteResult = true;
	if (callbackFunction != 0)
		callbackFunction( true );
	return;
}

/**
  *	Writes the offset file (_texture_atlas_offset.off) into the base directory
  *
  * @return boolean indicating whether the generation of the offset file was successful or not
  */
bool VTTAtlas::WriteOffsetFile( void ) const
{
	if (IsNull() || mImages.size() == 0)
		return false;

	string path = mConfiguration.GetBasePath();
	ofstream offset_file;

	float x_off = 0.0f;
	float y_off = 0.0f;
	float x_factor = 0.0f;
	float y_factor = 0.0f;

	const UINT32 *image_area;

	offset_file.open( path + VTT_FILE_SEPARATOR + "_texture_atlas_offset.off" );

	map<string, VTTImage*>::const_iterator it;

	for (it = mNamesToImagesMap.begin(); it != mNamesToImagesMap.end(); it++)
	{
		image_area = it->second->GetPackNodeVector()[0]->GetArea();

		x_off = static_cast<float>( image_area[0] ) / mSideLength;
		y_off = 1.0f - (static_cast<float>( image_area[3] ) / mSideLength);

		x_factor = static_cast<float>( image_area[2] - image_area[0] ) / mSideLength;
		y_factor = static_cast<float>( image_area[3] - image_area[1] ) / mSideLength;

		offset_file << it->first << " " << x_off << " " << y_off << " " << x_factor << " " << y_factor << "\n";
	}

	offset_file.close();

	return true;
}

/**
  *	Tests whether two rectangles intersect and returns true if this is the case, or false if not.
  * The rectangles are provided as const pointers to arrays with four elements which have the order x_min, y_min, x_max + 1, y_max + 1
  *
  * @param r1 const pointer to the first rectangle
  * @param r2 const pointer to the second rectangle
  * @return true if the two rectangles intersect, or false if not
  */
bool rectIntersectsRect( const UINT32 *r1, const UINT32 *r2 )
{
	return !(r1[0] >= r2[2] || r1[2] <= r2[0] || r1[1] >= r2[3] || r1[3] <= r2[1]);
}